<?php

class fobject
{
	public $fields = NULL;     // properties
	private $td;

	public function init ($store, $data=NULL)
	{
		$this->store = $store;
		$this->code = $store->code;
		$this->data  = $data;
		$this->td = $store->td;
	} // end of function init

	public static function load ($store, $row)
	{
		$td = $store->td;
		if (isset($row['type'])) {
			$type = $row['type'];

			$t = $td->getType($type);
			if (!$t) $t = $td->new_type(array('name'=>$type, 'type' => $store->basetype));

			$self = $td->new_inst($t);
			$self->init($store);
			$self->id = $row['oid'];
			$self->type = $type;
			$self->data = unserialize($row['data']);
			$self->ctime = $row['ctime'];
			$self->mtime = $row['mtime'];
			$self->is_deleted = $row['is_deleted'];
		} else {
			$t = $td->getType($store->basetype);
			$self = $td->new_inst($t);
			$self->init($store);
			$self->id = NULL;
			$self->type = NULL;
			$self->data = $row;
			$self->ctime = NULL;
			$self->mtime = NULL;
			$self->is_deleted = 0;
		}
		return $self;
	} // end of function load

	public function delete ($class_name) {
		// например для начала в такой постановке
		$this->store->delete( $this->id );
		return $this->store->delProperty( $this->id, $class_name );
	} // enf of function delete

	public function save ()
	{
		$row = array(
			'oid'   => $this->id,
			'type'  => $this->getTypeName(),
			'data'  => serialize($this->data),
			'ctime' => $this->ctime,
			//'mtime' => $this->mtime,
			'mtime' => NULL,
			'is_deleted' => $this->is_deleted,
		);

		$res = $this->store->save($row);
		if (isset($res['oid'])) {
			$this->id = $res['oid'];
		}
		$this->store->putObject($this);
		return $res;
	} // end of function save

	public function updateSense ($class_name, $criteria=NULL)
	{
		// обновит содержимое таблицы класса
		debug(__METHOD__."($class_name, [criteria])","all");
/*
		$collection = $this->hackGetCollection($class_name);
		$props = $collection->sync($this);
*/
		$props = $this->getProps(&$class_name);
		$oid = isset($props['oid']) ? $props['oid'] : $this->id;
		unset($props['oid']);
		$this->store->db->begin();
		$this->store->delProperty($oid, $class_name);
		$this->store->addProperty($oid, $class_name, $props);
		$this->store->db->end();

		return $props;
	} // end of function updateSense


	public function inSense ($class_name, $criteria=NULL)
	{
		/*
		debug(__METHOD__."($class_name, [criteria])","all");
		$rs = $this->store->findProperty($this->id, $class_name, $criteria);
		$propCount = 0;
		$props = array();
		while ($rs && ($row = $rs->next()))
		{
			while (list($field, $value) = each($row)) {
				if (!is_int($field)) {
					$props[$propCount][$field] = $value;
				}
			}
			$propCount++;
		}
		if (empty($props)) {
			$collection = $this->hackGetCollection($class_name);
			$props = $collection->add($this);
		}
		*/
		$props = $this->getProps(&$class_name);

		$oid = isset($props['oid']) ? $props['oid'] : $this->id;
		unset($props['oid']);
		return $this->store->addProperty($oid, $class_name, $props);
	} // end of function inSense

	// HACK : 2007-12-06 : lucky
	//
	private function hackGetCollection(&$class_name)
	{
		if (!is_object($class_name)) {
			$cclass_name = 'c'.$class_name;
			$td = $this->store->db->ctx->td;
			if ($collection = $td->new_inst($cclass_name)) {
				$collection->store = $this->store;
			} elseif ($collection = $td->new_inst($class_name)) {
				$collection->store = $this->store;
			} else {
				$struct = array('name' => $class_name, 'type' => 'collection', 'fields' => $this->getFields(), 'implement' => 'collections/collection.php#collection');
				$t = $td->new_type($struct);
				$collection = $td->new_inst($t);
				$collection->store = $this->store;
			}
			$collection->parents = $collection->__type->getTypeRefs();
			$collection->type = $collection->__type->getName();
			$collection->fields = $collection->__type->getFields();
			$collection->db = $collection->__type->meta['db'];
		} else {
			$collection = $class_name;
			$class_name = $collection->getTypeName();
		}
		//var_Dump(array_keys(get_object_vars($collection))); die();
		//var_dump($collection->fields);die();
		return $collection;
	}

	private function hackGetProps(&$class_name)
	{
		if (!is_object($class_name)) {
			$cclass_name = 'c'.$class_name;
			$td = $this->td;

			if ($collection = $td->new_inst($cclass_name)) {
				$collection->store = $this->store;
			} elseif ($collection = $td->new_inst($class_name)) {

				$collection->store = $this->store;

			} else {

				// не нашли никакой тип создаем пустой
				$struct = array('name' => $class_name, 'type' => 'collection', 'fields' => $this->getFields(), 'implement' => 'collections/collection.php#collection');
				$t = $td->new_type($struct);
				$collection = $td->new_inst($t);
				$collection->store = $this->store;
			}
			$collection->parents = $collection->__type->getTypeRefs();
			$collection->type = $collection->__type->getName();
			$collection->fields = $td->getType($cclass_name)->getFields(); //$collection->__type->getFields();
			$collection->db = $collection->__type->meta['db'];
		} else {
			$collection = $class_name;
			$class_name = $collection->getTypeName();
		}
		//var_Dump(array_keys(get_object_vars($collection))); die();

		//die();
		$props = $this->fieldsToProps($collection->fields);
		return $props;
	}

	private function getProps(&$class_name)
	{
		if (!is_object($class_name)) {
			$cclass_name = 'c'.$class_name;
			$td = $this->td;

			if ($type = $td->getType($cclass_name)) {

			} elseif ($type = $td->getType($class_name)) {

			} else {
				// не нашли никакой тип, получается не знаем где взять свойства
				/*
				$struct = array('name' => $class_name, 'type' => 'collection', 'fields' => $this->getFields(), 'implement' => 'collections/collection.php#collection');
				$type = $td->new_type($struct);
				*/
				return false;
			}
			$fields = $type->getFields(); //$collection->__type->getFields();

		} else {
			return false;
		}

		$props = $this->fieldsToProps($fields);
		return $props;
	}


	private function fieldsToProps($fields)
	{
		$props = array();
		$data = $this->data;
		foreach ($fields as $t) {
			$prop = $t->meta;
			$prop_name = isset($prop['db']['name']) ? $prop['db']['name'] : $prop['name'];
			if (array_key_exists($prop_name, $data)) {
				$value = $data[$prop_name];
				$props[$prop_name] = $value;
			} elseif (array_key_exists('default', $prop)) {
				$value = $prop['default'];
				$props[$prop_name] = $value;
			} else {
				$value = NULL;
				//debug(__METHOD__."() property '{$prop_name}' value not found");
			}
		}
		return $props;
	} // end of function cast_object


	public function call ($name, $params=Array())
	{
		if (isset($this->methods[$name])) {
			$method = $this->methods[$name];
			//array_unshift($params, $this);
			$res = call_user_func_array($method, $params);
		} else {
			extract($params);
			$self = $this;
			$res = include $this->code . '/'.$name;
		}
		return $res;
	} // end of function call

	public function buildFields ()
	{
		$fields = array();
		$type   = $this->__type;
		$trs = $type->getTypeRefs();
		array_unshift($trs, $type);
		foreach ($trs as $tt) {
			$fields = array_merge($tt->getFields(), $fields);
		}
		return $fields;
	} // end of function buildFields

	public function getFields ()
	{
		if (!isset($this->fields)) { // если нет инфы, пытаемся использовать тип
			$this->fields = $this->buildFields();
		}
		if (empty($this->fields)) { // если нет инфы, пытаемся использовать собственные данные
			$td = $this->td;
			foreach (array_keys($this->data) as $name) {
				$this->fields[$name] = array('name' => $name);
			}
		}
		return $this->fields;
	} // end of function getFields

	// преобразования объект -> my object
	public function cast_from ($o, $ask_other=true)
	/**
	 * Преобразовать объекта к своему типу.
	 *
	 * @param mixed $o объект
	 * @param mixed $ask_other служебный влажок.
	 * @access public
	 * @return void
	 */
	{
		if ($ask_other) $props = $o->cast_to($this, false);
		if (empty($props)) {
			$types = $o->getTypes();
			foreach ($types as $type) {
				$cast_func = array($this, 'cast_from_'.$type->getName());
				if (is_callable($cast_func)) {
					$props = call_user_func($cast_func, $o);
					break;
				}
			}
		}
		return $props;
	} // end of function cast
	// преобразования my object -> объект
	public function cast_to ($o, $ask_other=true)
	{
		if ($ask_other) $props = $o->cast_from($this, false);
		if (empty($props)) {
			$types = $o->getTypes();
			foreach ($types as $type) {
				$cast_func = array($this, 'cast_to_'.$type->getName());
				if (is_callable($cast_func)) {
					$props = call_user_func($cast_func, $o);
					break;
				}
			}
		}
		return $props;
	} // end of method cast_to_fobject

	public function cast_to_collection ($o)
	{
		$props = array();
		$data = $this->data;
		foreach ($o->getFields() as $t) {
			$prop = $t->meta;
			$prop_name = isset($prop['db']['name']) ? $prop['db']['name'] : $prop['name'];
			if (array_key_exists($prop_name, $data)) {
				$value = $data[$prop_name];
				$props[$prop_name] = $value;
			} elseif (array_key_exists('default', $prop)) {
				$value = $prop['default'];
				$props[$prop_name] = $value;
			} else {
				$value = NULL;
				//debug(__METHOD__."() property '{$prop_name}' value not found");
			}
		}
		return $props;
	} // end of function cast_object

	// --- types --- {{{
	// my type hierarhy as list
	public function getTypes ()
	{
		return $this->__type->getTypeRefs();
	} // end of function getTypes
	public function getTypeName ()
	{
		return $this->__type->getName();
	} // end of function getType

	public function getMeta ()
	{
		$meta = array();
		$type   = $this->__type;
		$trs = $type->getTypeRefs();
		array_unshift($trs, $type);
		foreach ($trs as $tt) {
			$meta = array_merge_recursive($tt->meta, $meta);
		}
		return $meta;
	} // end of method getMeta
	// --- types --- }}}


}


